<script setup>
import * as Icon from '@element-plus/icons-vue'
import {exportApiData} from "@/api/common/index.js";

/**
 * 定义emit事件
 */
const emits = defineEmits([
    'query',
    'selectChange',
    'update:modelValue',
])

/**
 * 定义props 属性
 */
const props = defineProps({
  modelValue: {
    type: Object,
    default: () => {
      return {
        page: 1,
        limit: 50,
        total: 0,
        orders: [],
        filters: [],
        condition: {},
        url: '',
      }
    }
  },
  // 表格配置
  tableInfo: {
    type: Object,
    default: () => {
      return {
        // 是否显示选中数量
        showSelectCount: true,
        // 是否显示导出按钮
        showExportBtn: true,
      }
    }
  },
  // 列配置
  columnInfo: {
    type: Array,
    default: () => []
  },
  // 分页数量配置
  pageSizes: {
    type: Array,
    default() {
      return [50, 100, 200, 500]
    }
  },
  // 分页按钮数量配置
  pagerCount: {
    type: Number,
    default: 5
  },
  // 分页标签配置
  layout: {
    type: String,
    default: 'total, sizes, prev, pager, next, jumper'
  },
  // 数据id
  rowId: {
    type: String,
    default: 'id'
  },
  // 计量单位
  unit: {
    type: String,
    default: '条'
  },
  // 表格整体距离底部高度
  marginBottomHeight: {
    type: [String,Number],
    default: 240
  },
})

/**
 * 接收未使用的属性
 */
const $attrs = useAttrs()

/**
 * VxeTable引用
 */
const VTable = ref()

/**
 * 表格高度
 */
const height = ref(document.documentElement.clientHeight - props.marginBottomHeight)

/**
 * 复选框选中行数
 */
const selectRows = ref([])

/**
 * 导出按钮loading
 */
const exportLoading = ref(false)

/**
 * 计算分页查询参数
 */
const pageQuery = computed(
    () =>{
      return props.modelValue
    }
)

/**
 * 计算表格属性
 */
const tableAttrs = computed(() => {
  const attrs = {
    size: 'mini',
    resizable:true,
  }
  if (!props.tableInfo || !(props.tableInfo.treeConfig || props.tableInfo['tree-config'])){
    attrs.seqConfig = {
      seqMethod: ({rowIndex})=> (pageQuery.value.page-1) * pageQuery.value.limit+rowIndex+1
    }
  }
  return { ...props.tableInfo, ...attrs }
})

/**
 * 计算分页属性
 */
const pageAttrs = computed(()=>{
  const attrs = {
    pagerCount: props.pagerCount,
    layout: props.layout,
    pageSizes: props.pageSizes,
    background: true,
    total: pageQuery.value.total,
  }
  return { ...attrs }
})

/*watch(
    pageQuery,
    (val) => {
      console.log(val)
    },
    {
      deep:true,
      immediate: true
    }
)*/

/**
 * 组件挂载后
 */
onMounted(() => {
  window.onresize = () => {
    height.value = document.documentElement.clientHeight - props.marginBottomHeight
  }
})

/**
 * 复选框选中逻辑
 */
const select = (info, callback) => {
  const { checked, records, reserves } = info
  if (checked) {
    if (reserves.length === 0) {
      // 未翻页
      selectRows.value = records
    } else {
      // 翻页后
      selectRows.value = [
        ...records,
        ...reserves
      ]
    }
    selectRows.value = distArray(selectRows.value)
  } else {
    callback(info)
  }
}

/**
 * 数组去重
 */
const distArray = (arr) => {
  const map = new Map()
  arr.forEach((item) => {
    if (!map.has(item[props.rowId])) { map.set(item[props.rowId], item) }
  })
  return [...map.values()]
}

/**
 * 复选框选中事件
 */
const selectChange = ({ checked, row, records, reserves }) => {
  select(Object.assign({ checked, row, records, reserves }), ({ row }) => {
    const index = selectRows.value.findIndex((item, index) => item[props.rowId] === row[props.rowId])
    if (index !== -1) { selectRows.value.splice(index, 1) }
  })
  emits('selectChange', selectRows.value)
}

/**
 * 复选框全选事件
 */
const selectAll = ({ checked, records, reserves }) => {
  select(Object.assign({ checked, records, reserves }), ({ records }) => {
    // 取消全选
    selectRows.value = []
    clearSelectAll()
  })
  emits('selectChange', selectRows.value)
}

/**
 * 单选框选中事件
 */
const radioChange = ({row}) => {
  selectRows.value = [row]
  emits('selectChange', selectRows.value)
}

/**
 * 清空复选框事件
 */
const clearSelectAll = () => {
  selectRows.value = []
  VTable.value.clearCheckboxRow()
  VTable.value.clearCheckboxReserve()
  VTable.value.clearRadioRow()
  emits('selectChange', selectRows.value)
}

/**
 * 父组件引用
 */

/**
 * 操作列按钮点击事件
 */
const handleClickEvent = ($event,$parent,{ method, row },$children) => {
  const type = typeof ($parent[method])
  const types = typeof(props.tableInfo[method])
  // 由于组件嵌套原因、单层parent可能不行、所以增加tableInfo
  if (type && type === 'function') $parent[method]($event,row)
  else if (types && types === 'function') props.tableInfo[method]($event,row)
  else throw new Error(`${method} is not function`)
}

/**
 * 导出Excel
 */
const handleExportClick = async () => {
  exportLoading.value = true
  if (selectRows.value.length > 0) {
    //导出选中数据
    exportVTableData(selectRows.value)
  } else {
    //导出所有
    if (!props.modelValue.url)
      return ElMessage({message: '没有表格数据接口！', type: 'warning',})
    const exportData = await exportAllData()
    exportVTableData(exportData)
  }
  exportLoading.value = false
}

/**
 * 调用接口获取后台数据
 */
const exportAllData = () => {
  const LIMIT = 1000;
  const apiResultList = []
  const total = props.modelValue.total
  const pages = Math.ceil(total / LIMIT)
  const pageQuery = JSON.parse(JSON.stringify(props.modelValue));
  const exportUrl = pageQuery.url
  for (let i = 1; i <= pages; i++) {
    pageQuery.page = i;
    pageQuery.limit = LIMIT;
    const api = new Promise((resolve, reject) => {
      exportApiData(exportUrl, pageQuery).then(({code, data}) => {
        if (code === 200) {
          resolve(data)
        } else {
          reject(data)
        }
      })
    })
    apiResultList.push(api);
  }
  return new Promise((resolve, reject) => {
    let data = []
    Promise.all(apiResultList).then((res) => {
      res.forEach(ele => {
        data = data.concat(ele.records)
      })
      resolve(data)
    }).catch(() => {
      reject(data)
    })
  })
}

/**
 * 前端导出 // throw new Error(`前端导出功能正在拼命开发中！`)
 */
const exportVTableData = (exportData = []) => {
  if (exportData.length === 0) return ElMessage({message: '没有数据,不可导出！', type: 'warning',})
  // 如果采用tree-node渲染，row会渲染多出children字段，导出数据重复，故删掉children数据
  /*exportData.forEach((e) => {
    if (e.children && e.children.length) {
      delete e.children;
    }
  })*/
  ElMessageBox.prompt(
      "请输入文件名：",
      "导出到Excel文件",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        inputPattern: /\S/,
        inputErrorMessage: "文件名不能为空",
      }
  )
      .then(({ value }) => {
        VTable.value.exportData({
          filename: value,
          sheetName: 'Sheet1',
          type: "xlsx",
          isHeader: true,
          isFooter: true,
          data: exportData,
          columnFilterMethod({ column, $columIndex }) {
            return !(column.type || column.title === "操作")
          }
        })
      })
}

/**
 * 当前页改变事件
 */
const updateCurrentPage = () => {
  emits('query')
}
/**
 * 分页数据量改变事件
 */
const updatePageSize = () => {
  emits('query')
}

defineExpose({
  VTable
})
</script>

<template>
  <div class="container">
    <div class="table">
      <vxe-table
          ref="VTable"
          :height="height"
          v-bind="Object.assign(tableAttrs,$attrs)"
          v-on="Object.assign($attrs)"
          @checkbox-change="selectChange"
          @checkbox-all="selectAll"
          @radio-change="radioChange"
      >
        <template v-for="(column,index) in columnInfo">

          <!-- 操作列 -->
          <vxe-column
              v-if="column.operate"
              v-bind="{...column}" v-slot="{row}"
              :title="column.title || '操作'"
              :fixed="column.fixed || 'right'"
              :align="column.center || 'center'"
          >
            <el-button
                v-bind="Object.assign(item)"
                :key="index"
                :icon="Icon[item.icon]"
                v-for="(item,index) in column.options"
                :style="{color: item.color || '#409EFF' }"
                @click="handleClickEvent($event,$parent,{ method: item.method, row })">{{ item.btnName }}
            </el-button>
          </vxe-column>

          <!-- 配置列 -->
          <vxe-column v-if=" !(column.diyColumn || column.operate)" v-bind="Object.assign(column,$attrs)">
            <!-- 表头插槽 -->
            <template v-if="column.proxyColumnHeader" #header>
              <slot :name="column.proxyColumnHeader" />
            </template>
            <!-- 配置列插槽 -->
            <template v-if="column.proxyColumn" v-slot="scope">
              <slot
                  :name="column.field || column.proxyColumn"
                  :row="scope.row"
                  :rowIndex="scope.rowIndex"
                  :column="scope.column"
                  :columnIndex="scope.columnIndex"
              />
            </template>
          </vxe-column>
          <!-- 自定义列 -->
          <slot v-else :name="column.field || column.diyColumn" ></slot>
        </template>
        <slot></slot>

      </vxe-table>
      <footer>
        <div class="left" v-if="tableInfo.showSelectCount">
          <span class="selected">已选择</span>
          <span class="length">{{ selectRows.length }}</span>{{ unit }}
          <span class="clear" @click="clearSelectAll">清空</span>
        </div>
        <div class="right">
          <el-pagination
              v-bind="Object.assign(pageAttrs,$attrs)"
              v-model:currentPage="pageQuery.page"
              @update:currentPage="updateCurrentPage"
              v-model:pageSize="pageQuery.limit"
              @update:pageSize="updatePageSize"
          />
          <el-button
              v-if="tableInfo.showExportBtn"
              type="danger" size="small"
              :loading="exportLoading"
              class="export" plain
              @click="handleExportClick"
          ><el-icon class="el-icon-my-export"></el-icon>导出</el-button>
        </div>
      </footer>
    </div>
  </div>
</template>

<style scoped lang="scss">
.container{
  height: 100%;
  margin-top: 3px;
  display: flex;
  //flex-direction: column;
}
/*header{
  flex: 0 0 40px;
  display: flex;
  background: #fff;
  margin: 5px 5px 0;
  border: 1px solid #dfe6ec;
  border-radius: 5px;
  align-items: center;
  label{
    flex: 0 0 40px;
    font-weight: normal;
    white-space: nowrap;
    margin-left: 20px;
  }
  .el-button,{
    margin-left: 10px;
  }
  //.el-input{
  //  margin-left: 10px;
  //}
}*/

.table{
  padding:3px 10px 0;
  width: 100%;
}
.tableTools{
  display: flex;
  justify-content: flex-start;
  margin: 8px 10px 5px 10px;
}

footer{
  margin-top: 10px;
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-items: center;
  white-space: nowrap;
  .left{
    display: flex;
    align-items: center;
    .selected{
      color: rgb(191, 188, 190);
    }
    .length{
      margin: 0 10px;
      font-size: 13px;
      color: rgb(18, 131, 234);
    }
    img{
      height: 30px;
      margin-top: -5px;
    }
    .clear {
      margin-left: 10px;
      color: #00a7ff;
      cursor: pointer;
    }
  }
  .right{
    display: flex;
    align-items: center;
    :deep(.el-pagination__jump) {
      margin-left: 14px;
    }
    .el-button--danger.is-plain {
      background-color: rgba(0, 167, 255, 0.1)!important;
      border-color: #00a7ff;
      color: #00a7ff !important;
      display: flex;
      align-items: center;
    }
    .el-button--mini, .el-button--mini.is-round {
      padding: 5px 20px;
    }
    :deep(.export){
      margin-left: 10px;
      .el-icon-my-export {
        background: url("../assets/export.png") no-repeat;
        width: 16px;
        height: 16px;
      }
    }
  }
}
.el-row{
  margin-left: 10px;
}
</style>
